package session

import (
	"geeorm/clause"
	"reflect"
)

// Insert one or more records in database
func (s *Session) Insert(values ...interface{}) (int64, error) {
	recordValues := make([]interface{}, 0)
	for _, value := range values {
		table := s.Model(value).RefTable()
		s.clause.Set(clause.INSERT, table.Name, table.FieldNames)
		recordValues = append(recordValues, table.RecordValues(value))
	}

	s.clause.Set(clause.VALUES, recordValues...)
	sql, vars := s.clause.Build(clause.INSERT, clause.VALUES)
	result, err := s.Raw(sql, vars...).Exec()
	if err != nil {
		return 0, err
	}

	return result.RowsAffected()
}

// Insert2 one or more records in database
func (s *Session) Insert2(values ...interface{}) (int64, error) {
	recordValues := make([]interface{}, 0)
	for _, value := range values {
		table := s.Model(value).RefTable()
		s.clause.Set(clause.INSERT, table.Name, table.FieldNames)
		recordValues = append(recordValues, table.RecordValues(value))
	}

	s.clause.Set(clause.VALUES, recordValues...)
	sql, vars := s.clause.Build(clause.INSERT, clause.VALUES)

	result, err := s.ExecWithVals(sql, vars...)
	if err != nil {
		return 0, err
	}

	// 转为原来的对象
	// 通过反射获取到原来的对象
	//dest := reflect.Indirect(reflect.ValueOf(values[0]))
	//// 获取主键的值
	//id, err := result.LastInsertId()
	//if err != nil {
	//	return 0, err
	//}
	//// 将主键的值设置到原来的对象中
	//dest.FieldByName("ID").SetInt(id)
	return result.RowsAffected()
}

///部分	描述
//函数签名	func (s *Session) Find(values interface{}) error
//参数	values interface{}：用于存储从数据库中检索的记录的切片。
//反射	destSlice：存储数据库记录的切片。
//destType：元素类型，表示要创建的记录类型。
//table：包含表名和字段名的数据库表信息。
//查询准备	使用 s.clause.Set 方法设置查询参数，如 SELECT、WHERE、ORDER BY 和 LIMIT。
//使用 clause.Build 方法构建 SQL 查询和变量绑定。
//执行查询	使用 s.Raw(sql, vars...).QueryRows() 执行 SQL 查询。
//迭代结果	使用 for rows.Next() 循环遍历查询结果的每一行。
//创建新的 dest 反射值，表示一个记录对象。
//创建空的 values 切片，用于扫描数据库中的字段值。
//遍历表的字段名，并为每个字段的地址将其添加到 values 中。
//扫描数据	使用 rows.Scan(values...) 扫描数据库查询结果的当前行并将数据映射到 values 中。
//如果扫描出现错误，将返回错误。
//记录追加	使用 destSlice.Set(reflect.Append(destSlice, dest)) 将映射的记录追加到 values 中。
//关闭查询结果	使用 rows.Close() 关闭查询结果。
//错误处理	如果在查询、扫描或其他步骤中发生错误，将返回相应的错误。

//destSlice.Type().Elem()
//获取切片的单个元素的类型 destType，使用 reflect.New() 方法创建一个 destType 的实例，作为 Model() 的入参，映射出表结构 RefTable()。
//2）根据表结构，使用 clause 构造出 SELECT 语句，查询到所有符合条件的记录 rows。
//3）遍历每一行记录，利用反射创建 destType 的实例 dest，将 dest 的所有字段平铺开，构造切片 values。
//4）调用 rows.Scan() 将该行记录每一列的值依次赋值给 values 中的每一个字段。
//5）将 dest 添加到切片 destSlice 中。循环直到所有的记录都添加到切片 destSlice 中。

// Find gets all eligible records
func (s *Session) Find(values interface{}) error {
	destSlice := reflect.Indirect(reflect.ValueOf(values))
	destType := destSlice.Type().Elem()
	table := s.Model(reflect.New(destType).Elem().Interface()).RefTable()

	s.clause.Set(clause.SELECT, table.Name, table.FieldNames)
	sql, vars := s.clause.Build(clause.SELECT, clause.WHERE, clause.ORDERBY, clause.LIMIT)
	rows, err := s.Raw(sql, vars...).QueryRows()
	if err != nil {
		return err
	}

	for rows.Next() {
		dest := reflect.New(destType).Elem()
		var values []interface{}
		for _, name := range table.FieldNames {
			values = append(values, dest.FieldByName(name).Addr().Interface())
		}
		if err := rows.Scan(values...); err != nil {
			return err
		}
		destSlice.Set(reflect.Append(destSlice, dest))
	}
	return rows.Close()
}

// Find2 gets all eligible records
func (s *Session) Find2(values interface{}) error {
	noCertainValue := reflect.ValueOf(values)
	destSlice := reflect.Indirect(noCertainValue) // 1. 根据声明的slice类型，创建一个新的slice
	destType := destSlice.Type().Elem()           // 2. 获取到slice的元素类型。Elem() 方法来获取其元素的类型 It panics if the type's Kind is not Array, Chan, Map, Pointer, or Slice.
	// 3. 根据slice类型，获取到表结构
	table := s.Model(reflect.New(destType).Elem().Interface()).RefTable()

	s.clause.Set(clause.SELECT, table.Name, table.FieldNames)
	sql, vars := s.clause.Build(clause.SELECT, clause.WHERE, clause.ORDERBY, clause.LIMIT)
	rows, err := s.Raw(sql, vars...).QueryRows()
	if err != nil {
		return err
	}

	for rows.Next() {
		dest := reflect.New(destType).Elem()
		var values []interface{}
		for _, fieldName := range table.FieldNames {
			values = append(values, dest.FieldByName(fieldName).Addr().Interface())
		}
		if err := rows.Scan(values...); err != nil { // 调用 rows.Scan() 将该行记录每一列的值依次赋值给 values 中的每一个字段。（根据table.FieldNames的顺序）
			return err
		}
		destSlice.Set(reflect.Append(destSlice, dest))
	}
	return rows.Close()
}
